package org.dicr.sys.linux;

import java.io.*;
import java.util.*;

import org.dicr.traffic.source.*;
import org.dicr.util.net.*;

/**
 * IPTables Traffic Counter
 * 
 * @author Igor A Tarasov &lt;java@dicr.org&gt;
 * @version 060712
 * @see IPTables
 */
public class IPTablesData implements Serializable {

	private static final long serialVersionUID = 1L;

	/** Network-address */
	private Net addr = null;

	/** Traffic rule */
	protected boolean opened = false;

	/** Input bytes */
	protected long bytesIn = 0;

	/** Output bytes */
	protected long bytesOu = 0;

	/** Rule number for input traffic */
	protected int ruleIn = 0;

	/** Rule number for output traffic */
	protected int ruleOu = 0;

	/**
	 * Constructor
	 */
	protected IPTablesData() {
		super();
	}

	/**
	 * Constructor
	 * 
	 * @param address
	 *            address
	 * @param isOpened
	 *            true if opened, false if closed
	 */
	protected IPTablesData(Net address, boolean isOpened) {
		super();
		if (address == null) throw new IllegalArgumentException("null address");
		this.addr = address;
		this.opened = isOpened;
	}

	/**
	 * Return address
	 * 
	 * @return address for which traffic is counted
	 */
	public Net getAddress() {
		return this.addr;
	}

	/**
	 * Return input bytes
	 * 
	 * @return bytes count, input from address
	 */
	public long getBytesIn() {
		return this.bytesIn;
	}

	/**
	 * Set input bytes
	 * 
	 * @param theBytesIn
	 *            bytes count, input from address
	 */
	protected void setBytesIn(long theBytesIn) {
		if (theBytesIn < 0) throw new IllegalArgumentException("bytesIn: " + theBytesIn);
		this.bytesIn = theBytesIn;
	}

	/**
	 * Return output bytes
	 * 
	 * @return bytes count, output to address
	 */
	public long getBytesOu() {
		return this.bytesOu;
	}

	/**
	 * Set output bytes
	 * 
	 * @param theBytesOu
	 *            bytes count, output to address
	 */
	protected void setBytesOu(long theBytesOu) {
		if (theBytesOu < 0) throw new IllegalArgumentException("bytesOu: " + theBytesOu);
		this.bytesOu = theBytesOu;
	}

	/**
	 * Return input rule number
	 * 
	 * @return input rule position number in chain
	 */
	public int getRuleIn() {
		return this.ruleIn;
	}

	/**
	 * Set input rule number.
	 * 
	 * @param position
	 *            input rule position number in chain
	 */
	protected void setRuleIn(int position) {
		if (position < 0) throw new IllegalArgumentException("ruleIn: " + position);
		this.ruleIn = position;
	}

	/**
	 * Return output rule number
	 * 
	 * @return output rule position number in chain
	 */
	public int getRuleOu() {
		return this.ruleOu;
	}

	/**
	 * Set output rule number
	 * 
	 * @param position
	 *            output rule position number in chain
	 */
	protected void setRuleOu(int position) {
		if (position < 0) throw new IllegalArgumentException("ruleOu: " + position);
		this.ruleOu = position;
	}

	/**
	 * Check traffic allowed
	 * 
	 * @return true if traffic is accepted, or false if rejected
	 */
	public boolean isOpened() {
		return this.opened;
	}

	/**
	 * Convert to traffic
	 * 
	 * @return traffic elements for this.counter
	 */
	public Collection<TrafficElement> toTraffic() {
		Collection<TrafficElement> traffic = new ArrayList<>();
		// output traffic from address
		TrafficElement el = new TrafficElement();
		el.setSrc(this.addr.getIp());
		el.setSrcMask(this.addr.getMask());
		el.setDst(null);
		// reverse input/output for address view
		el.setBytes(this.bytesIn);
		traffic.add(el);
		// input traffic of address
		el = new TrafficElement();
		el.setSrc(null);
		el.setDst(this.addr.getIp());
		el.setDstMask(this.addr.getMask());
		// reverse for address point of view
		el.setBytes(this.bytesOu);
		traffic.add(el);
		return traffic;
	}

	/**
	 * Translate to string
	 * 
	 * @return string
	 */
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder(this.getClass().getSimpleName() + "{");
		sb.append("addr=" + this.addr + ",");
		sb.append("opened=" + this.opened + ",");
		sb.append("bytesIn=" + this.bytesIn + ",");
		sb.append("bytesOu=" + this.bytesOu + ",");
		sb.append("ruleIn=" + this.ruleIn + ",");
		sb.append("ruleOu=" + this.ruleOu);
		sb.append("}");
		return sb.toString();
	}

	/**
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int PRIME = 31;
		int result = 1;
		result = PRIME * result + ((this.addr == null) ? 0 : this.addr.hashCode());
		return result;
	}

	/**
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj) return true;
		if (obj == null) return false;
		if (getClass() != obj.getClass()) return false;
		final IPTablesData other = (IPTablesData) obj;
		if (this.addr == null) {
			if (other.addr != null) return false;
		} else if (!this.addr.equals(other.addr)) return false;
		return true;
	}
}
